package com.huawei.cloud.phone.management.infrastructure.phone;

import com.huawei.cloud.phone.management.common.BeanFactory;
import com.huawei.cloud.phone.management.common.GlobalConfig;
import com.huawei.cloud.phone.management.domain.phone.Phone;
import com.huawei.cloud.phone.management.domain.phone.*;
import com.huawei.cloud.phone.management.infrastructure.ClientFactory;
import com.huawei.cloud.phone.management.infrastructure.db.mapper.PhoneMapper;
import com.huawei.cloud.phone.management.infrastructure.db.mapper.UserPhoneMapper;
import com.huaweicloud.sdk.core.exception.ClientRequestException;
import com.huaweicloud.sdk.cph.v1.CphClient;
import com.huaweicloud.sdk.cph.v1.model.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@AllArgsConstructor
@Component
public class PhoneGatewayImpl implements PhoneGateway {
    private final PhoneMapper phoneMapper;

    private final UserPhoneMapper userPhoneMapper;

    @Override
    public List<Phone> getPhonesFromManager(@NonNull String serverId) {
        CphClient client = ClientFactory.getCphClient();
        ListCloudPhonesRequest request = new ListCloudPhonesRequest();
        request.setServerId(serverId);
        List<Phone> phones = new LinkedList<>();
        int startIndex = 0;
        int queryNumber = 100;
        while (true) {
            request.setLimit(queryNumber);
            request.setOffset(startIndex);
            ListCloudPhonesResponse response = client.listCloudPhones(request);
            List<com.huaweicloud.sdk.cph.v1.model.Phone> phoneList = Optional.ofNullable(response)
                    .map(ListCloudPhonesResponse::getPhones).orElse(Collections.emptyList());
            phones.addAll(phoneList.stream().map(com.huaweicloud.sdk.cph.v1.model.Phone::getPhoneId)
                    .map(phoneId -> getPhoneInfo(phoneId, client))
                    .map(this::convertPhone)
                    .collect(Collectors.toList()));
            if (phoneList.size() != queryNumber) {
                break;
            }
            startIndex += queryNumber;
        }
        return phones;
    }

    @Override
    public Optional<Phone> getPhoneFromManager(@NonNull String phoneId) {
        CphClient client = ClientFactory.getCphClient();
        try {
            ShowCloudPhoneDetailResponse phoneInfo = getPhoneInfo(phoneId, client);
            return Optional.of(convertPhone(phoneInfo));
        } catch (ClientRequestException e) {
            log.error("get phone failed", e);
            if ("CPS.0005".equals(e.getErrorCode())) {
                return Optional.empty();
            }
            throw e;
        }
    }

    @Override
    public List<Phone> getPhonesByServerIds(@NonNull List<String> serverIds) {
        if (serverIds.isEmpty()) {
            return Collections.emptyList();
        }
        return phoneMapper.getPhonesByServerIds(serverIds);
    }

    @Override
    public List<Phone> getPhonesByPhoneIds(@NonNull List<String> phoneIds) {
        if (CollectionUtils.isEmpty(phoneIds)) {
            return Collections.emptyList();
        }
        return phoneMapper.getPhonesByPhoneIds(phoneIds);
    }

    @Override
    public List<String> getNotManagerServerId(@NonNull List<String> serverIds) {
        if (serverIds.isEmpty()) {
            return Collections.emptyList();
        }
        Set<String> managerServerIds = new HashSet<>(phoneMapper.getServerIds(serverIds));
        return serverIds.stream().filter(serverId -> !managerServerIds.contains(serverId)).collect(Collectors.toList());
    }

    @Override
    public List<String> getNotManagerPhoneId(@NonNull List<String> phoneIds) {
        if (phoneIds.isEmpty()) {
            return Collections.emptyList();
        }
        Set<String> managerPhoneIds = new HashSet<>(phoneMapper.getPhoneIds(phoneIds));
        return phoneIds.stream().filter(phoneId -> !managerPhoneIds.contains(phoneId)).collect(Collectors.toList());
    }

    @Override
    public void savePhones(@NonNull List<Phone> phones) {
        phoneMapper.mergerPhones(phones);
    }

    @Override
    public void clearPhones(@NonNull String serverId) {
        phoneMapper.deletePhonesByServerId(serverId);
    }

    @Override
    public List<Phone> getReadyPhones() {
        return phoneMapper.getPhonesByStatus(PhoneStatus.RUNNING, PhoneUseStatus.NORMAL);
    }

    @Override
    public boolean updatePhoneUseStatus(@NonNull String phoneId, @NonNull PhoneUseStatus useStatus) {
        return phoneMapper.updatePhoneUseStatus(phoneId, useStatus) != 0;
    }

    @Override
    public void updatePhoneUseStatus(@NonNull PhoneUseStatus useStatus, long timeout) {
        phoneMapper.updateUseStatus(useStatus, timeout);
    }

    @Override
    public List<UserPhone> getUserPhones(@NonNull String userId) {
        return userPhoneMapper.getUserPhone(userId);
    }

    @Override
    public boolean addUserPhones(@NonNull List<UserPhone> userPhones) {
        return userPhoneMapper.addUserPhone(userPhones) == userPhones.size();
    }

    private Phone convertPhone(ShowCloudPhoneDetailResponse phone) {
        Phone result = new Phone();
        result.setPhoneId(phone.getPhoneId());
        result.setPhoneName(phone.getPhoneName());
        result.setServerId(phone.getServerId());
        result.setImageId(phone.getImageId());
        result.setStatus(PhoneStatus.getStatus(phone.getStatus()));
        result.setUseStatus(PhoneUseStatus.UNKNOWN);

        List<PhoneAccessInfo> phoneAccessInfoList = Optional.of(phone)
                .map(ShowCloudPhoneDetailResponse::getAccessInfos)
                .orElse(Collections.emptyList());
        GlobalConfig globalConfig = BeanFactory.getBean(GlobalConfig.class);
        for (PhoneAccessInfo info : phoneAccessInfoList) {
            result.setAccessIp(info.getAccessIp());
            if (globalConfig.getAppServerKey().equals(info.getType())) {
                result.setAccessAppPort(info.getAccessPort());
            } else if (globalConfig.getH5ServerKey().equals(info.getType())) {
                result.setAccessH5Port(info.getAccessPort());
            }
        }
        return result;
    }

    private ShowCloudPhoneDetailResponse getPhoneInfo(String phoneId, CphClient client) {
        ShowCloudPhoneDetailRequest detailRequest = new ShowCloudPhoneDetailRequest().withPhoneId(phoneId);
        return client.showCloudPhoneDetail(detailRequest);
    }
}
